﻿#include <learn/ais/ais_context.h>
#include <learn/gl/gl_context.h>
#include <learn/gl/gl_renderer.h>

using namespace xi;
using namespace xi::gl;

GL_Renderer::GL_Renderer(GL_Context *ctx) : AIS_Renderer(), m_ctx(ctx)
{
    if (m_ctx != nullptr)
        m_ctx->gen_array(m_id);
}

GL_Renderer::~GL_Renderer()
{
    m_ctx->remove_array(m_id);
}

void GL_Renderer::draw(AIS_Object *obj) const
{
    // 向着色器传递数据
    m_ctx->shader()->set_value("model", Mat4x4<float>::identity());
    m_ctx->shader()->set_value("view", m_ctx->context()->info()->camera->view());
    m_ctx->shader()->set_value("projection", m_ctx->context()->info()->camera->projection());
    m_ctx->shader()->set_value("viewPos", m_ctx->context()->info()->camera->position());
}

GL_PointRenderer::GL_PointRenderer(GL_Context *ctx) : GL_Renderer(ctx)
{
    // 生成着色器
    m_ctx->gen_shader("point");
}

void GL_PointRenderer::update(AIS_Object *obj)
{
    m_ctx->bind_array(m_id);

    // 注意转换为 float 类型
    AIS_Point *castObj = static_cast<AIS_Point *>(obj);
    Vec3f pos = convert<float>(*castObj->point());

    glBufferData(GL_ARRAY_BUFFER, sizeof(Vec3f), pos.data, GL_DYNAMIC_DRAW);

    // vertex Positions
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3f), (void *)0);

    m_ctx->unbind_array();
}

void GL_PointRenderer::draw(AIS_Object *obj) const
{
    m_ctx->bind_shader("point");

    // 传入变换矩阵
    GL_Renderer::draw(obj);

    AIS_Point *castObj = static_cast<AIS_Point *>(obj);
    if (castObj->status() == AIS_Object::Status::Hovered)
    {
        m_ctx->shader()->set_value("color", hoveredColor);
        m_ctx->shader()->set_value("alpha", hoveredOpacity);
        glPointSize(hoveredSize);
    }
    else if (castObj->status() == AIS_Object::Status::Pressed)
    {
        m_ctx->shader()->set_value("color", pressedColor);
        m_ctx->shader()->set_value("alpha", pressedOpacity);
        glPointSize(pressedSize);
    }
    else
    {
        m_ctx->shader()->set_value("color", normalColor);
        m_ctx->shader()->set_value("alpha", normalOpacity);
        glPointSize(normalSize);
    }

    // 配置绘制信息，允许后绘制的内容覆盖之前的内容
    glDepthFunc(GL_LEQUAL);

    // 开始绘制
    m_ctx->bind_array(m_id);

    glDrawArrays(GL_POINTS, 0, 1);

    m_ctx->unbind_array();

    // 恢复默认
    glPointSize(1);
    glDepthFunc(GL_LESS);
}

GL_PointCloudRenderer::GL_PointCloudRenderer(GL_Context *ctx) : GL_Renderer(ctx)
{
    // 生成着色器
    m_ctx->gen_shader("point");
}

void GL_PointCloudRenderer::update(AIS_Object *obj)
{
    m_ctx->bind_array(m_id);

    // 注意转换为 float 类型
    AIS_PointCloud *castObj = static_cast<AIS_PointCloud *>(obj);
    std::vector<Vec3f> points(castObj->points().size());
    for (int i = 0; i < points.size(); i++)
        points[i] = convert<float>(castObj->points()[i]);

    // 创建缓冲
    glBufferData(GL_ARRAY_BUFFER, points.size() * sizeof(Vec3f), points.data(), GL_DYNAMIC_DRAW);

    // vertex Positions
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3f), (void *)0);

    m_ctx->unbind_array();
}

void GL_PointCloudRenderer::draw(AIS_Object *obj) const
{
    m_ctx->bind_shader("point");

    // 传入变换矩阵
    GL_Renderer::draw(obj);

    m_ctx->shader()->set_value("color", normalColor);
    m_ctx->shader()->set_value("alpha", normalOpacity);

    // 配置绘制信息，允许后绘制的内容覆盖之前的内容
    glPointSize(normalSize);
    glDepthFunc(GL_LEQUAL);

    // 开始绘制
    m_ctx->bind_array(m_id);

    AIS_PointCloud *castObj = static_cast<AIS_PointCloud *>(obj);
    glDrawArrays(GL_POINTS, 0, castObj->points().size());

    m_ctx->unbind_array();

    // 恢复默认
    glPointSize(1);
    glDepthFunc(GL_LESS);
}

GL_VectorRenderer::GL_VectorRenderer(GL_Context *ctx) : GL_Renderer(ctx)
{
    // 生成着色器，带有几何着色器
    m_ctx->gen_shader("vector", true);
}

void GL_VectorRenderer::update(AIS_Object *obj)
{
    m_ctx->bind_array(m_id);

    // 注意转换为 float 类型
    AIS_Vector *castObj = static_cast<AIS_Vector *>(obj);
    Vec3f origin = convert<float>(*castObj->origin());
    Vec3f vec = convert<float>(*castObj->vector());

    // 创建空的缓冲
    glBufferData(GL_ARRAY_BUFFER, 2 * sizeof(Vec3f), NULL, GL_DYNAMIC_DRAW);

    // 分批填充
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(Vec3f), origin.data);
    glBufferSubData(GL_ARRAY_BUFFER, sizeof(Vec3f), sizeof(Vec3f), vec.data);

    // 原点位置
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3f), (void *)0);

    // 向量方向
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3f), (void *)(sizeof(Vec3f)));

    m_ctx->unbind_array();
}

void GL_VectorRenderer::draw(AIS_Object *obj) const
{
    m_ctx->bind_shader("vector");

    // 传入变换矩阵
    GL_Renderer::draw(obj);

    // 向量方向作为颜色
    AIS_Vector *castObj = static_cast<AIS_Vector *>(obj);
    Vec3f color = convert<float>(*castObj->vector()).normalize();
    color.x() = std::abs(color.x());
    color.y() = std::abs(color.y());
    color.z() = std::abs(color.z());
    m_ctx->shader()->set_value("color", color);

    if (castObj->status() == AIS_Object::Status::Hovered)
        m_ctx->shader()->set_value("alpha", hoveredOpacity);
    else if (castObj->status() == AIS_Object::Status::Pressed)
        m_ctx->shader()->set_value("alpha", pressedOpacity);
    else
        m_ctx->shader()->set_value("alpha", normalOpacity);

    // 关闭深度缓冲，防止抗锯齿产生缝隙
    // m_ctx->glEnable(GL_POLYGON_SMOOTH);
    // m_ctx->glDepthMask(GL_FALSE);
    // m_ctx->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glDepthFunc(GL_ALWAYS);

    // 设置深度范围固定为 0，这样绘制部分深度将会为 0，避免被覆盖
    glDepthRange(0, 0);

    // 开始绘制
    m_ctx->bind_array(m_id);

    glDrawArrays(GL_POINTS, 0, 1);

    m_ctx->unbind_array();

    // 恢复默认
    glDepthFunc(GL_LESS);
    glDepthRange(0.0, 1.0);
}

GL_CurveRenderer::GL_CurveRenderer(GL_Context *ctx) : GL_Renderer(ctx)
{
    // 生成着色器
    m_ctx->gen_shader("curve");
}

void GL_CurveRenderer::update(AIS_Object *obj)
{
    m_ctx->bind_array(m_id);

    // 注意由于辅助曲线的存在，需要更新采样数
    AIS_Curve *castObj = static_cast<AIS_Curve *>(obj);
    auto samples = castObj->sample(sampleCount);
    sampleCount = samples.size();

    // 传送数据
    glBufferData(GL_ARRAY_BUFFER, samples.size() * sizeof(Vec3f), samples.data(), GL_DYNAMIC_DRAW);

    // vertex Positions
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3f), (void *)0);

    m_ctx->unbind_array();
}

void GL_CurveRenderer::draw(AIS_Object *obj) const
{
    m_ctx->bind_shader("curve");

    // 传入变换矩阵
    GL_Renderer::draw(obj);

    AIS_Curve *castObj = static_cast<AIS_Curve *>(obj);
    if (castObj->status() == AIS_Object::Status::Hovered)
    {
        m_ctx->shader()->set_value("color", hoveredColor);
        m_ctx->shader()->set_value("alpha", hoveredOpacity);
        glLineWidth(hoveredWidth);
    }
    else if (castObj->status() == AIS_Object::Status::Pressed)
    {
        m_ctx->shader()->set_value("color", pressedColor);
        m_ctx->shader()->set_value("alpha", pressedOpacity);
        glLineWidth(pressedWidth);
    }
    else
    {
        m_ctx->shader()->set_value("color", normalColor);
        m_ctx->shader()->set_value("alpha", normalOpacity);
        glLineWidth(normalWidth);
    }

    // 开始绘制
    m_ctx->bind_array(m_id);

    glDrawArrays(GL_LINE_STRIP, 0, sampleCount);

    m_ctx->unbind_array();

    // 恢复默认
    glLineWidth(1);
}

GL_MeshRenderer::GL_MeshRenderer(GL_Context *ctx)
    : GL_Renderer(ctx), diffuseTex(GL_Context::Green2d), specularTex(GL_Context::White2d),
      reflectTex(GL_Context::Black2d), reflactTex(GL_Context::Black2d)
{
    // 生成着色器
    m_ctx->gen_shader("mesh");
    m_data = [this](AIS_Object *obj) -> Mesh_Data {
        AIS_Mesh *castObj = static_cast<AIS_Mesh *>(obj);
        auto samples = castObj->mesh();
        this->m_count = samples->indices.size(); // 注意记录顶点数
        return samples;
    };
}

void GL_MeshRenderer::update(AIS_Object *obj)
{
    m_ctx->bind_array(m_id);

    // 创建空的缓冲
    auto samples = m_data(obj);
    auto n = samples->vertices.size();
    glBufferData(GL_ARRAY_BUFFER, n * (sizeof(Vec3f) * 2 + sizeof(Vec2f)), NULL, GL_DYNAMIC_DRAW);

    // 载入顶点和索引数据
    glBufferSubData(GL_ARRAY_BUFFER, 0, n * sizeof(Vec3f), samples->vertices.data());
    glBufferSubData(GL_ARRAY_BUFFER, n * sizeof(Vec3f), n * sizeof(Vec3f), samples->normals.data());
    glBufferSubData(GL_ARRAY_BUFFER, 2 * n * sizeof(Vec3f), n * sizeof(Vec2f), samples->texcoords.data());
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, samples->indices.size() * sizeof(unsigned int), samples->indices.data(),
                 GL_DYNAMIC_DRAW);

    // vertex Positions
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3f), (void *)0);

    // vertex normals
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3f), (void *)(n * sizeof(Vec3f)));

    // vertex texture coords
    glEnableVertexAttribArray(2);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vec2f), (void *)(2 * n * sizeof(Vec3f)));
    // offsetof 计算变量偏移地址

    m_ctx->unbind_array();
}

void GL_MeshRenderer::draw(AIS_Object *obj) const
{
    m_ctx->bind_shader("mesh");

    // 传入变换矩阵
    GL_Renderer::draw(obj);

    // 光照信息和纹理贴图
    m_ctx->shader()->set_value("dirLight.direction", direction);
    m_ctx->shader()->set_value("dirLight.ambient", ambient);
    m_ctx->shader()->set_value("dirLight.diffuse", diffuse);
    m_ctx->shader()->set_value("dirLight.specular", specular);

    m_ctx->activate_tex(0);
    m_ctx->bind_tex2d(diffuseTex);
    m_ctx->shader()->set_value("material.diffuse", 0);

    m_ctx->activate_tex(1);
    m_ctx->bind_tex2d(specularTex);
    m_ctx->shader()->set_value("material.specular", 1);

    m_ctx->activate_tex(2);
    m_ctx->bind_tex2d(reflectTex);
    m_ctx->shader()->set_value("material.reflect_", 2);

    m_ctx->activate_tex(3);
    m_ctx->bind_tex2d(reflactTex);
    m_ctx->shader()->set_value("material.reflact", 3);

    // 设置斑马纹
    if (obj->shade_mode() == obj->Zebra)
        m_ctx->shader()->set_value("zebra", zebra);
    else
        m_ctx->shader()->set_value("zebra", 0);

    if (obj->status() == AIS_Object::Status::Hovered)
        m_ctx->shader()->set_value("alpha", hoveredOpacity);
    else if (obj->status() == AIS_Object::Status::Pressed)
        m_ctx->shader()->set_value("alpha", pressedOpacity);
    else
        m_ctx->shader()->set_value("alpha", normalOpacity);

    // 开始绘制
    m_ctx->bind_array(m_id);

    glDrawElements(GL_TRIANGLES, m_count, GL_UNSIGNED_INT, 0);

    m_ctx->unbind_array();
}

GL_SurfaceRenderer::GL_SurfaceRenderer(GL_Context *ctx) : GL_MeshRenderer(ctx)
{
    // 生成着色器
    m_ctx->gen_shader("mesh");
    m_data = [this](AIS_Object *obj) -> Mesh_Data {
        AIS_Surface *castObj = static_cast<AIS_Surface *>(obj);
        auto samples = castObj->sample(sampleCount);
        m_count = samples->indices.size(); // 注意记录顶点数
        return samples;
    };
}

GL_FrameRenderer::GL_FrameRenderer(GL_Context *ctx) : GL_Renderer(ctx)
{
    // 生成着色器
    m_ctx->gen_shader("frame");
}

void GL_FrameRenderer::update(AIS_Object *obj)
{
    m_ctx->bind_array(m_id);

    AIS_Frame *castObj = static_cast<AIS_Frame *>(obj);

    // 根据矩形大小生成帧缓冲
    m_ctx->remove_frame(m_id);
    auto rect = castObj->rect();
    m_ctx->gen_frame(m_id, rect.width, rect.height);

    int width = m_ctx->context()->info()->width;
    int height = m_ctx->context()->info()->height;

    // 转二维屏幕坐标
    Vec2f pos{2.0f * rect.left / width - 1.0f, 2.0f * (height - (rect.top + rect.height)) / height - 1.0f};
    Vec2f size{2.0f * rect.width / width, 2.0f * rect.height / height};

    float vertices[6][4] = {{pos.x(), pos.y(), 0.0f, 0.0f},
                            {pos.x() + size.x(), pos.y(), 1.0f, 0.0f},
                            {pos.x() + size.x(), pos.y() + size.y(), 1.0f, 1.0f},
                            {pos.x(), pos.y(), 0.0f, 0.0f},
                            {pos.x(), pos.y() + size.y(), 0.0f, 1.0f},
                            {pos.x() + size.x(), pos.y() + size.y(), 1.0f, 1.0f}};

    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices[0], GL_STATIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void *)0);

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void *)(2 * sizeof(float)));

    m_ctx->unbind_array();
}

void GL_FrameRenderer::draw(AIS_Object *obj) const
{
    // 绘制一个空背景
    m_ctx->bind_frame(m_id);

    glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    m_ctx->unbind_frame();

    // 开始绘制框架
    m_ctx->bind_shader("frame");

    // 传入变换矩阵
    GL_Renderer::draw(obj);

    m_ctx->shader()->set_value("alpha", alpha);
    m_ctx->shader()->set_value("tex", 0);

    // 开始绘制
    m_ctx->bind_array(m_id);

    // 设置深度范围固定为 0，这样绘制部分深度将会为 0，避免被覆盖
    glDepthFunc(GL_ALWAYS);
    glDepthRange(0, 0);

    // 激活纹理，绑定到帧缓冲附加的纹理，绑定纹理坐标到纹理变量
    m_ctx->activate_tex(0);
    glBindTexture(GL_TEXTURE_2D, m_ctx->frame_tex(m_id));
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

    glDrawArrays(GL_TRIANGLES, 0, 6);

    m_ctx->unbind_tex();
    m_ctx->unbind_array();

    // 恢复设置
    glDepthRange(0.0, 1.0);
    glDepthFunc(GL_LESS);
}